from http.cookies import SimpleCookie
from typing import Optional, Any, Callable
import json
from urllib.parse import urljoin

from parsel import Selector, SelectorList

DEFAULT_JSON_DECODER = json.loads
JSONDecoder = Callable[[str], Any]

class Response:
    def __init__(self,
                 url: str,
                 method: str,
                 *,
                 request_url: str,
                 encoding: str="",
                 html: str="",
                 status: int = -1,
                 metadata: dict,
                 cookies,
                 headers,
                 json: Callable = None,
                 read: Callable = None,
                 text: Callable = None):
        '''
        :param url: current url
        :param method:  request method, only support get and post
        :param encoding:
        :param html:
        :param status:
        :param request_url: request url
        :param metadata: metadata
        :param cookies: cookies
        :param headers:
        :param _json:
        :param _read:
        :param _text:
        '''
        self.request_url = request_url
        self._callback_result = None
        self._url: str = url
        self._method: str = method
        self._encoding: str = encoding
        self._status: int = status
        self._html: str = html
        self._metadata = metadata
        self._cookies = cookies
        self._headers = headers
        self._ok: bool = (200 <= self.status <= 299)

        self._json: Callable = json
        self._read: Callable = read
        self._text: Callable = text

    @property
    def callback_result(self):
        return self._callback_result

    @callback_result.setter
    def callback_result(self, value):
        self._callback_result = value

    @property
    def ok(self) -> bool:
        return self._ok

    @ok.setter
    def ok(self, value: bool):
        self._ok = value

    @property
    def encoding(self) -> str:
        return self._encoding

    @property
    def url(self) -> str:
        return self._url

    @property
    def method(self) -> str:
        return self._method

    @property
    def metadata(self):
        return self._metadata

    @property
    def html(self):
        return self._html

    @html.setter
    def html(self, value):
        self._html = value

    @property
    def status(self) -> int:
        return self._status

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self) -> dict:
        if isinstance(self._cookies, SimpleCookie):
            ret_cookies = {}
            for key, value in self._cookies.items():
                ret_cookies[key] = value
            return ret_cookies
        else:
            return self._cookies

    @cookies.setter
    def cookies(self, value):
        self._cookies = value

    @property
    def selector(self) -> Selector:
        sel: Selector = None
        if self._html:
            sel = Selector(text=self._html)
        return sel

    @property
    def xpath(self):
        return self.selector.xpath

    @property
    def re(self):
        return self.selector.re

    @property
    def re_first(self):
        return self.selector.re_first

    def urljoin(self, url: str):
        return urljoin(self.url, url)

    async def json(
        self,
        *,
        encoding: str = None,
        loads: JSONDecoder = DEFAULT_JSON_DECODER,
        content_type: Optional[str] = "application/json",
    ) -> Any:
        """Read and decodes JSON response."""
        return await self._json(
            encoding=encoding, loads=loads, content_type=content_type
        )

    async def read(self) -> bytes:
        """Read response payload."""
        return await self._read()

    async def text(
        self, *, encoding: Optional[str] = None, errors: str = "strict"
    ) -> str:
        """Read response payload and decode."""
        return await self._text(encoding=encoding, errors=errors)

    def __repr__(self):
        return f"<Response url[{self._method}]: {self._url} status:{self._status}>"
